<?php
/**
 *  mahuohuo 本地模拟的存储类
 * @see https://gitee.com/foglede
 * @copyright https://gitee.com/foglede
 * @author https://gitee.com/foglede
 * 4
 */
class SaeStorage {
    private $basedomain = 'http://127.0.0.1/mahuohuo/mahuohuostorage.php?';
    private $storagepath = array(SAE_TMP_PATH, __CLASS__);
    
    public function __construct() {
        //文件存储在 SAE_TMP_PATH /SaeStorage/$domain 文件夹下
        //文件描述以JSON存入 SAE_TMP_PATH /storage 文件夹下
        //mahuohuostorage.php 读取JSON 设置头然后发送
    }
    
   /**
     * 文件名左侧所有的'/'都会被过滤掉。
    *  @author mahuohuo
     * @param string $domain: 存储域,在在线管理平台.storage页面可进行管理
     * @param  string $destFileName: 文件名
     * @param string $content: 文件内容,支持二进制数据
     * @param  int $size: 写入长度,默认为不限制
     * @param  array $attr: 文件属性，可设置的属性请参考 MahuohuoSaeStorage::setFileAttr() 方法
     * @param bool $compress: 是否gzip压缩。如果设为true，则文件会经过gzip压缩后再存入Storage，常与$attr=array('encoding'=>'gzip')联合使用
     * @return mixed|string 
     **/
    public function write($domain,$destFileName,$content,$size=-1, $attr=array(), $compress = false){
        $domain = trim($domain);
        $destFileName = iconv('UTF-8','GBK',$destFileName);
        
        //$destFileName = ltrim($destFileName,'/');
        if(empty ($domain)){return FALSE; } // mb_strlen($content) == 0 这句没多大意义
        
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();        
        
        $pinfo = $this->storagepath + array($pinfonames[0]->getName() => $domain,'filename' => $destFileName);
        $pinfo = implode(DIRECTORY_SEPARATOR, $pinfo);
        //(unset) $pinfonames;
        //(unset) $pinfo;
        
        if($size > -1){
            $content = mb_substr($content, 0, $size, 'utf-8');//我反正没懂SAE说的 支持二进制数据 是怎么回事
        }
        
        if((boolean)$compress){
            $content = gzencode($content,5);
        }
        
        if( 
        (wincache_is_dir(dirname($pinfo))/**  **/ or mkdir(dirname($pinfo)) )  and !(is_bool($content)) and file_put_contents($pinfo, $content, LOCK_EX) > 0 ){
            if(count($attr) != 0){
                $this->setFileAttr($domain, $destFileName,$attr);
            }
            return $this->basedomain.http_build_query(array($pinfonames[0]->getName() => $domain,'filename' => $destFileName));
        }  else {
            return FALSE;
        }
    }    
    
    /**
     * 将文件上传入存储
     *
     * 注意：文件名左侧所有的'/'都会被过滤掉。
     * @author mahuohuo
     * @param string $domain 存储域,在在线管理平台.storage页面可进行管理
     * @param string $destFileName 目标文件名
     * @param string $srcFileName 源文件名
     * @param array $attr 文件属性，可设置的属性请参考 SaeStorage::setFileAttr() 方法
     * @param bool $compress 是否gzip压缩。如果设为true，则文件会经过gzip压缩后再存入Storage，常与$attr=array('encoding'=>'gzip')联合使用
     * @return string 写入成功时返回该文件的下载地址，否则返回false
     * 
     */
    public function upload( $domain, $destFileName, $srcFileName, $attr = array(), $compress = false ) {        
        $srcFileName = wincache_file_get_contents($_files[$srcFileName]['tmp_name']);        
        return $this->write($domain, $destFileName, $srcFileName,$size=-1,$attr,$compress);
    }
    
    public function read($domain,$filename){
        $domain = trim($domain);
        
        $filename = mb_convert_encoding($filename, 'GBK', 'auto');
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();        
        $pinfo = $this->storagepath + array($pinfonames[0]->getName() => $domain,$pinfonames[1]->getName() => $filename);
        $pinfo = implode(DIRECTORY_SEPARATOR, $pinfo);
        $pinfo = urldecode($pinfo);
        return wincache_file_get_contents($pinfo);
    }
    
    public function getDomainCapacity($domain){
        $domain = trim($domain);
        
        $result = $this->getList($domain);
        
        $domain = array($domain);
        $domain = array_combine($domain,$domain);
        
        $domain = implode(DIRECTORY_SEPARATOR, $domain);
        $resultcount = 0;
        array_map(function ($d) use ($domain,&$resultcount) {
            $resultcount += sprintf('%u',wincache_filesize($domain.DIRECTORY_SEPARATOR.$d));
        },$result);
        return $resultcount;
    }


    public function fileExists($domain,$filename){
        $domain = trim($domain);
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();        
        $pinfo = $this->storagepath + array($pinfonames[0]->getName() => $domain,$pinfonames[1]->getName() => $filename);
        $pinfo = implode(DIRECTORY_SEPARATOR, $pinfo);
        return wincache_file_exists($pinfo);
    }
    
    public function getAttr($domain,$filename,$attr=array()) {
        $domain = trim($domain);
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();        
        $pinfo = $this->storagepath + array($pinfonames[0]->getName() => $domain,$pinfonames[1]->getName() => $filename);
        $pinfo = implode(DIRECTORY_SEPARATOR, $pinfo);
                
        if(file_exists($pinfo.'.mahuohuofile')){
            $domain = wincache_file_get_contents($pinfo.'.mahuohuofile');
            $domain = json_decode($domain, TRUE);
            if(array_key_exists('expires', $domain)){
                $pinfonames['expires'] = $domain['expires'];
            }
        }
        $pinfonames = array('fileName'=>$filename,'length'=>wincache_filesize($pinfo),'datetime'=>filectime($pinfo));
        
        if(count($attr) != 0){
           $attr = array_combine($attr, $attr);
           $pinfonames = array_intersect_key($pinfonames,$attr); 
        }
        
        return $pinfonames ;
    }

    public function delete($domain,$filename){
        $domain = trim($domain);
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();        
        $pinfo = $this->storagepath + array($pinfonames[0]->getName() => $domain,$pinfonames[1]->getName() => $filename);
        $pinfo = implode(DIRECTORY_SEPARATOR, $pinfo);
        if(wincache_file_exists($pinfo.'.mahuohuofile')){
            unlink($pinfo.'.mahuohuofile');
        }        
        return wincache_file_exists($pinfo) && unlink($pinfo);
    }
    ///这两个函数只是前缀相同，但是WINCACHE的缓存BUG认为是一个函数
    public function deleteFolder($domain,$path) {
        $domain = trim($domain);
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();        
        $pinfo = $this->storagepath + array($pinfonames[0]->getName() => $domain,$pinfonames[1]->getName() => $filename);
        $pinfo = implode(DIRECTORY_SEPARATOR, $pinfo);
        if(!wincache_is_dir($pinfo)){
            return FALSE;
        }
        
        array_map(function($key){
           unlink($key);
        },glob($pinfo.DIRECTORY_SEPARATOR.'*'));
        return rmdir($pinfo);
    }
    
    public function getFilesNum($domain,$path = null) {
        return count($this->getListByPath($domain,$path));
    }
    
    public function getListByPath($domain,$path= null,$limit = 100,$offset = 0,$fold=TRUE){
        $domain = trim($domain);
        
        $result = $this->getList($domain);
        if(!empty ($path)){
            $result = array_filter($result,function($key) use ($path){
                return is_bool(strpos($key,  $path));//斜杠啊斜杠
            });
        }
        return $result;
    }

    public function getUrl( $domain, $filename ) {
        $domain = trim($domain);
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();
        return $this->basedomain.http_build_query(array($pinfonames[0]->getName() => $domain,$pinfonames[1]->getName() => $filename));
    }

    public function setFileAttr($domain,$filename,$attr = array('expires'=>'','encoding'=>'','type'=>'')){
        $domain = trim($domain);
        
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();        
        $pinfo = $this->storagepath + array($pinfonames[0]->getName() => $domain);
        $pinfo = implode(DIRECTORY_SEPARATOR, $pinfo).DIRECTORY_SEPARATOR;
        if(!wincache_is_dir($pinfo)){
            return FALSE;
        }
        
        //潜在问题：文件到底存在否在本段代码中是无法判断的，也没必要判断
        array_map(function($key) use ($filename,$attr){
            if(fnmatch(basename($filename),basename($key))){
                file_put_contents($key.'.mahuohuofile', json_encode($attr));// 文件名+mahuohuo扩展名
            }
        }, glob($pinfo.(strcmp(dirname($filename),'.') === 0 ? '' : dirname($filename).DIRECTORY_SEPARATOR).'*' ,
        GLOB_NOCHECK | GLOB_MARK | GLOB_BRACE | GLOB_ERR));
        //scandir($pinfo) 使用该函数也要把路径补全并且要 pop 前面2个项3个点
        return TRUE;        
    }
    
    public function getList($domain,$prefix='*',$limit=10,$offset=0) {
        $domain = trim($domain);
        $domain = array($domain);
        $domain = array_combine($domain,$domain);
        $domain = $this->storagepath + $domain;
        
        $domainpath = implode(DIRECTORY_SEPARATOR, $domain);
                
        $domain = glob($domainpath.$prefix,GLOB_BRACE | GLOB_MARK);
        
        $result = array();
        
        do{
            $d = array_shift($domain);  
            if(wincache_is_dir($d)){
                $domain = array_merge($domain,glob($d.$prefix,GLOB_BRACE | GLOB_MARK));
            }else{
                $d = str_replace(array($domainpath.DIRECTORY_SEPARATOR,'.mahuohuodomain','.mahuohuofile'), '', $d);
                array_push($result,$d);  
            }
        }while ($d != NULL);
        
        $result = array_unique($result);
        array_pop($result);
        array_shift($result);
        
        return $result;//还有两个参数可以切割一下

    } 


    public function setDomainAttr($domain,$attr=array('expires'=>'','allowReferer'=>'','private'=>'','tag'=>'')){
        $domain = trim($domain);
        $pinfo = new ReflectionMethod($this,__FUNCTION__);
        $pinfonames = $pinfo->getParameters();
        $pinfo = $this->storagepath + array($pinfonames[0]->getName() => $domain);
        $pinfo = implode(DIRECTORY_SEPARATOR, $pinfo).DIRECTORY_SEPARATOR;
        file_put_contents($pinfo.$domain.'.mahuohuodomain', json_encode($attr));
        
        return TRUE;
    }
    
    /**
     * 获得错误代码
     *
     * @return int 返回错误代码
     */
    function errno() {
        return 0;
    }

    /**
     * 获得错误提示消息
     *
     * @return string 返回错误提示消息字符串
     */
    function errmsg() {
        return '<a href="http://www.mahuohuo.com/">mahuohuo本地环境居然出错了！没这个文件或者目录吧！</a>';
    }
        
}

?>
